package org.scboy.dbms.service.imp;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.scboy.common.cache.SystemConfig;
import org.scboy.coobi.sql.factory.EntityFactory;
import org.scboy.dbms.dao.BaseAreaCodeDao;
import org.scboy.dbms.dao.BaseAreaMappingDao;
import org.scboy.dbms.dao.BaseSystemConfigDao;
import org.scboy.dbms.loads.LoadAreaCode;
import org.scboy.dbms.service.BaseAreaCodeService;
import org.scboy.utils.caches.dbms.entity.AreaEntity;
import org.scboy.utils.entitys.coobi.Model;
import org.scboy.utils.entitys.coobi.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

/**
 * 区域编码
 * @author yanghao
 *
 */
@Service
public class BaseAreaCodeServiceImpl implements BaseAreaCodeService {

	//下载地址尝试次数
	private final int RE_COUNT=5;
	
	Log log=LogFactory.get();
	
	@Autowired
	EntityFactory factory;
	
	@Autowired
	LoadAreaCode areaCache;
	
	@Autowired
	SystemConfig config;
	
	/**
	 * 查询地区树
	 * @param parentCode
	 * @param params
	 * @return
	 */
	public List<Model> getTree(Model params){
		
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		List<Model> result=dao.select(params);
		for (Model model : result) {
			params.add("parentId", model.getString("id"));
			if(dao.count(params)>0){
				model.put("isLeaf", false);
				model.put("isParent", true);
			}else{
				model.put("isLeaf", true);
				model.put("isParent", false);
			}
		}
		return result;
		
	}
	
	public Page pageAreaCode(Model params) {
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		return dao.page(params);
	}
	
	/**
	 * 查询地区
	 * @param id
	 * @param params
	 * @return
	 */
	public Model get(String id){
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		return dao.get(Model.create().add("id", id));
	}
	
	/**
	 * 地区编码映射
	 * @param code 地区编码
	 * @param mappingCode 映射编码
 	 */
	public boolean addMapping(String code,String mappingCode,String mappingName,String areaId) {
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		BaseAreaMappingDao mappingDao=factory.getDao(BaseAreaMappingDao.class);
		Model areaCode=dao.get(Model.create().add("id", areaId));
		if(areaCode==null)return false;
		long count=mappingDao.count(Model.create().add("code", code).add("mappingCode", mappingCode));
		if(count>0) {
			return false;
		}else {
			long result=mappingDao.insert(
					Model.create().add("id", UUID.randomUUID().toString())
					.add("code", code)
					.add("areaName", areaCode.getString("address"))
					.add("mappingName", mappingName)
					.add("areaId", areaId)
					.add("mappingCode", mappingCode)
			);
			if(result>0) {
				return true;
			}else {
				return false;
			}
		}
	}
	
	public boolean updateMapping(String id,String areaId) {
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		BaseAreaMappingDao mappingDao=factory.getDao(BaseAreaMappingDao.class);
		Model mapping=mappingDao.get(Model.create().add("id", id));
		if(StrUtil.isEmpty(areaId)||mapping==null) {
			return false;
		}
		Model areaCode=dao.get(Model.create().add("id", areaId));
		if(areaCode==null)return false;
		long result=mappingDao.update(
				Model.create()
				.add("areaId", areaId)
				.add("areaName", areaCode.getString("address")),
				Model.create().add("id", id)
		);
		if(result>0) {
			return true;
		}else {
			return false;
		}
	}
	/**
	 * 删除映射关系
	 * @param id
	 * @return
	 */
	public boolean deleteMapping(String id) {
		BaseAreaMappingDao mappingDao=factory.getDao(BaseAreaMappingDao.class);
		long count=mappingDao.delete(Model.create().add("id", id));
		if(count>0) {
			return true;
		}else {
			return false;
		}
	}
	
	/**
	 * 分页查询映射编码
	 * @param code
	 * @param limit
	 * @return
	 */
	public Page page(Model model) {
		BaseAreaMappingDao mappingDao=factory.getDao(BaseAreaMappingDao.class);
		return mappingDao.page(model.orderBy("areaId"));
	}
	
	//获取URL中的年份
	public int getUrlYear(String url) {
		String [] urls=url.split("/");
		if(urls.length>2) {
			Integer year=Convert.toInt(urls[urls.length-2]);
			if(year==null) {
				return 0;
			}else {
				return year;
			}
		}else {
			return 0;
		}
	}
	
	@Async
	public void init(String url,String type){
		if(url.endsWith("index.html")) {
			int year=getUrlYear(url);
			loadProvince(url,year);
		}else {
			//检查国家统计局最新的地址信息
			Document doc;
			try {
				doc = Jsoup.parse( new URI(url).toURL(), 10000);
			}catch (IOException | URISyntaxException e) {
				throw new RuntimeException(e);
			}
			if("ALL".equals(type)) {
				
				BaseSystemConfigDao configDao=factory.getDao(BaseSystemConfigDao.class);
				//获取所有年份数据
				Elements aElements = doc.select(".wrapper-content .list-content li a");
				Integer year=Convert.toInt(config.getConfig("BASE_AREA_CODE_YEAR"));
				if(year==null) {
					year=0;
				}
				for (int i=aElements.size()-1; i>-1;i--) {
					Element element=aElements.get(i);
					url = element.absUrl("href");
					int newYear=getUrlYear(url);
					if(newYear>year) {
						year=newYear;
					}else {
						continue;
					}
					log.info("开始下载地址："+year);
					loadProvince(url,year);
					configDao.update(Model.create().add("code", "BASE_AREA_CODE_YEAR").add("configValue", year));
				}
			}else {
				//获取最新年份数据
				url = doc.select(".wrapper-content .list-content li a").first().attr("href");
				int year=getUrlYear(url);
				loadProvince(url,year);
			}
		}
		
	}
	
	public String getCode(String id) {
		int count=12-id.length();
		StringBuilder code=new StringBuilder(id);
		for (int i=0;i<count;i++) {
			code.append("0");
		}
		return code.toString();
	}
	
	@SuppressWarnings("unchecked")
	public void saveAreaCode(Model parent,Model model,int year) {
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		Model params=Model.create();
		params.add("id", model.getString("id"));
		params.add("name", model.getString("name"));
		params.add("level", model.getString("level"));
		params.add("areaType", model.getString("areaType"));
		params.add("code", getCode(model.getString("id")));
		params.add("state", 1);
		params.add("areaYear", year);
		if(parent!=null) {
			params.add("parentId", parent.getString("id"));
			params.add("parentCode", parent.getString("code"));
			params.add("address",  parent.getString("address") + model.getString("name"));
		}else {
			params.add("parentId", "root");
			params.add("parentCode", "");
			params.add("address",model.getString("name"));
		}
		//如果编码已经在数据库，进行更新
		long count=dao.count(Model.create().add("id", model.getString("id")));
		if(count>0) {
			dao.update(params);
		}else {
			dao.insert(params);
		}
		//更新缓存
		areaCache.update(params.toEntity(AreaEntity.class));
		//如果存在子项,处理子项
		if(model.containsKey("children")) {
			List<Model> childs=(List<Model>)model.get("children");
			for (Model child : childs) {
				saveAreaCode(params, child,year);
			}
		}
	}
	
	/**
	 * 读取省自治区编码表
	 * @param url 省url
	 * @param year 年份
	 * @return
	 */
	public void loadProvince(String url,int year){
		BaseAreaCodeDao dao=factory.getDao(BaseAreaCodeDao.class);
		//读取省地址数据
		Document doc;
		try {
			doc = Jsoup.parse( new URI(url).toURL(), 10000);
		}catch (IOException | URISyntaxException e) {
			throw new RuntimeException(e);
		}
		Elements provinceElements = doc.select(".provincetr a");
		for (Element pElement : provinceElements) {
			Model province=Model.create();
			String id=pElement.attr("href").replace(".html", "");
			province.add("id", id);
			String name=pElement.text();
			province.add("name",name);
			log.info("开始下载地址："+id+" "+name);
			String cityHref=pElement.absUrl("href");
			List<Model> citys=loadCity(cityHref);
			province.add("level", "AREA_LEVEL_001");
			province.add("areaType", "");
			province.add("children", citys);
			//省份下面所有子项的状态关闭
			dao.update(Model.create().add("state", 0),Model.create().LIKE("id", id+"%"));
			saveAreaCode(null, province,year);
		}
	}
	
	/**
	 * 读取地市编码表
	 * @param url
	 * @return
	 * @throws RuntimeException
	 */
	public List<Model> loadCity(String url) throws RuntimeException{
		List<Model> citys=new ArrayList<Model>();
		//读取省地址数据
		try {
			Document doc = Jsoup.parse( new URI(url).toURL(), 10000);
			Elements aCodes=doc.select(".citytr td:first-child");
			Elements aNames=doc.select(".citytr td:last-child");
			for (int i=0;i<aCodes.size();i++) {
				Model city=Model.create();
				Element aCode=aCodes.get(i);
				city.add("id", aCode.text().substring(0,4));
				
				Element aName=aNames.get(i);
				city.add("name", aName.text());
				//县地址
				if(aCode.select("a").size()>0 && StrUtil.isNotEmpty(aCode.select("a").first().absUrl("href"))) {
					String countyHref=aCode.select("a").first().absUrl("href");
					//失败重试
					int reCount=RE_COUNT;
					do {
						try {
							List<Model> countys=loadCounty(countyHref);
							city.add("children", countys);
							reCount=-1;
						} catch (Exception e) {
							log.error("地市下载失败重试"+(RE_COUNT-reCount)+"："+city.getString("id")+" "+city.getString("name"));
							reCount=reCount-1;
							//休息3秒重试
							Thread.sleep(1000*3);
						}
					} while (reCount>0);
				}else {
					city.add("children", new ArrayList<Model>());
				}
				city.add("level", "AREA_LEVEL_002");
				city.add("areaType", "");
				citys.add(city);
				log.info("-->完成地市下载地址："+city.getString("id")+" "+city.getString("name"));
			}
		}catch (IOException | InterruptedException  | URISyntaxException e) {
			throw new RuntimeException(e);
		}
		return citys;
	}
	
	/**
	 * 加载区县编码表
	 * @param url
	 * @return
	 * @throws  
	 */
	public List<Model> loadCounty(String url) {
		List<Model> countys=new ArrayList<Model>();
		try {
			Document doc = Jsoup.parse( new URI(url).toURL(), 10000);
			Elements aCodes=doc.select(".countytr td:first-child");
			Elements aNames=doc.select(".countytr td:last-child");
			for (int i=0;i<aCodes.size();i++) {
				Model county=Model.create();
				Element aCode=aCodes.get(i);
				county.add("id", aCode.text().substring(0,6));
				
				Element aName=aNames.get(i);
				county.add("name", aName.text());
				//乡镇地址
				if(aCode.select("a").size()>0  && StrUtil.isNotEmpty(aCode.select("a").first().absUrl("href"))) {
					String townHref=aCode.select("a").first().absUrl("href");
					//失败重试
					int reCount=RE_COUNT;
					do {
						try {
							List<Model> towns=loadTown(townHref);
							county.add("children", towns);
							reCount=-1;
						} catch (Exception e) {
							log.error("区县下载失败重试"+(RE_COUNT-reCount)+"："+county.getString("id")+" "+county.getString("name"));
							reCount=reCount-1;
							//休息3秒重试
							Thread.sleep(1000*3);
						}
					} while (reCount>0);
				}else {
					county.add("children", new ArrayList<Model>());
				}
				log.info("---->完成区县下载地址："+county.getString("id")+" "+county.getString("name"));
				county.add("level", "AREA_LEVEL_003");
				county.add("areaType", "");
				countys.add(county);
			}
		}catch (IOException | InterruptedException | URISyntaxException e ) {
			throw new RuntimeException(e);
		}
		return countys;
	}
	
	/**
	 * 读取乡镇编码表
	 * @param url
	 * @return
	 */
	public List<Model> loadTown(String url){
		List<Model> towns=new ArrayList<Model>();
		try {
			Document doc = Jsoup.parse( new URI(url).toURL(), 10000);
			Elements aCodes=doc.select(".towntr td:first-child");
			Elements aNames=doc.select(".towntr td:last-child");
			for (int i=0;i<aCodes.size();i++) {
				Model town=Model.create();
				Element aCode=aCodes.get(i);
				town.add("id", aCode.text().substring(0,9));
				
				Element aName=aNames.get(i);
				town.add("name", aName.text());
				//乡镇地址
				if(aCode.select("a").size()>0 && StrUtil.isNotEmpty(aCode.select("a").first().absUrl("href"))) {
					String villageHref=aCode.select("a").first().absUrl("href");
					//失败重试
					int reCount=RE_COUNT;
					do {
						try {
							List<Model> villages=loadVillage(villageHref);
							town.add("children", villages);
							reCount=-1;
						} catch (Exception e) {
							log.error("乡镇下载失败重试"+(RE_COUNT-reCount)+"："+town.getString("id")+" "+town.getString("name"));
							reCount=reCount-1;
							//休息3秒重试
							Thread.sleep(1000*3);
						}
					} while (reCount>0);
				}else {
					town.add("children", new ArrayList<Model>());
				}
				town.add("level", "AREA_LEVEL_004");
				town.add("areaType", "");
				towns.add(town);
			}
		}catch (IOException | InterruptedException  | URISyntaxException e) {
			throw new RuntimeException(e);
		}
		return towns;
	}
	
	/**
	 * 读取村社区编码表
	 * @param url
	 * @return
	 */
	public List<Model> loadVillage(String url){
		List<Model> villages=new ArrayList<Model>();
		try {
			Document doc = Jsoup.parse( new URI(url).toURL(), 10000);
			Elements aCodes=doc.select(".villagetr td:first-child");
			Elements aTypes=doc.select(".villagetr td:nth-child(2)");
			Elements aNames=doc.select(".villagetr td:last-child");
			for (int i=0;i<aCodes.size();i++) {
				Model village=Model.create();
				Element aCode=aCodes.get(i);
				village.add("id", aCode.text());
				Element aType=aTypes.get(i);
				village.add("areaType", "AREA_TYPE_"+aType.text());
				Element aName=aNames.get(i);
				village.add("name", aName.text());
				village.add("level", "AREA_LEVEL_005");
				villages.add(village);
			}
		}catch (IOException | URISyntaxException e) {
			throw new RuntimeException(e);
		}
		return villages;
	}
}
